#include "qchatserver.h"
#include "ui_qchatserver.h"


//add by dimon, 2014-03-18 16:50
#include <QMessageBox>
#include "common.h"
#include <QNetworkInterface>
#include <QHostAddress>
#include <QUdpSocket>
#include <QTcpSocket>


//数据库，套接字对象
QMyDB *db; //数据库对象
QUdpSocket *udpSocket; //UDP套接字

//在线用户列表onlineUser操作
QHash<quint32, QUser*> onlineUser; //在线用户列表
void setUserStatus(quint32 userid, quint8 status);
void setUserStatus(quint32 userid, quint8 status, quint32 ip, quint16 port);
void broadcast(QByteArray block);


//构造函数
QChatServer::QChatServer(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::QChatServer)
{
    ui->setupUi(this);

    //初始化界面
    ui->lblServerIP->clear();
    ui->lblServerPort->clear();

    //在线用户列表
    model.setHorizontalHeaderItem(0, new QStandardItem(QObject::tr("ID")));
    model.setHorizontalHeaderItem(1, new QStandardItem(QObject::tr("昵称")));
    model.setHorizontalHeaderItem(2, new QStandardItem(QObject::tr("IP")));
    model.setHorizontalHeaderItem(3, new QStandardItem(QObject::tr("端口")));
    ui->tblViewUser->setModel(&model);
    ui->tblViewUser->setColumnWidth(0, 80);
    ui->tblViewUser->setColumnWidth(1, 120);
    ui->tblViewUser->setColumnWidth(2, 120);
    ui->tblViewUser->setColumnWidth(3, 60);
    //设置选中时为整行选中
    ui->tblViewUser->setSelectionBehavior(QAbstractItemView::SelectRows);
    //设置单元格的属性为只读属性，即不能编辑
    ui->tblViewUser->setEditTriggers(QAbstractItemView::NoEditTriggers);
    //使用右键菜单
    ui->tblViewUser->setContextMenuPolicy(Qt::CustomContextMenu);

    ///服务状态，数据库初始化
    running = false;
    db = new QMyDB;
}

//析构函数
QChatServer::~QChatServer()
{
    delete ui;
}

//刷县在线用户列表
void QChatServer::tbviewRefresh(){
    model.removeRows(0, model.rowCount());
    int i = 0;
    foreach (quint32 userid, onlineUser.keys()) {
        QUser *pUser = onlineUser.value(userid);
        model.setItem(i, 0, new QStandardItem(QString::number(userid)));
        model.setItem(i, 1, new QStandardItem(pUser->name));
        model.setItem(i, 2, new QStandardItem(QHostAddress(pUser->ip).toString()));
        model.setItem(i, 3, new QStandardItem(QString::number(pUser->port)));
        i++;
    }
}

///启动/停止服务(Server, udpSocket)
void QChatServer::on_btnStart_clicked()
{
    if(!running){
        ///通知server监听到到的连接
        if(!server.listen(QHostAddress::Any, (qint16)8000)){
            QMessageBox::critical(NULL, tr("提示"), tr("TCP监听失败 %1.").arg(server.errorString()));
            return;
        }

        //显示服务器IP，端口
        QString ipAddress;
        QList<QHostAddress> ipAddressList = QNetworkInterface::allAddresses();

        for(int i=0; i< ipAddressList.size(); i++){ //use the first no-localhost IPv4 ipaddress
            if(ipAddressList.at(i) != QHostAddress::LocalHost &&
                    ipAddressList.at(i).toIPv4Address())
            {
                ipAddress = ipAddressList.at(i).toString();
                break;
            }
        }

        if(ipAddress.isEmpty()) // if we not find one, use IPv4 localhost
            ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
        ui->lblServerIP->setText(ipAddress);
        ui->lblServerPort->setText(QString::number(server.serverPort()));

        //UDP绑定
        udpSocket = new QUdpSocket(this);
        if(!udpSocket->bind(QHostAddress(ipAddress), (quint16)8001)){
            QMessageBox::critical(NULL, tr("提示"), tr("UDP绑定失败: %1.").arg(udpSocket->errorString()));
            return;
        }

        //关联 信号-槽
        //QIODevice::readyRead() 。 函数的原型是 readyRead() 而不是 readReady() !!!!!，之前我把名字错误的写成了readReady().
        //readyRead, readyWrite
        connect(udpSocket, SIGNAL(readyRead()), this, SLOT(onReadDatagrams()));

        ///启动之后设置‘按钮’的提示字符为 停止服务
        ui->btnStart->setText("停止服务");
        running = true;
    }
    else{ //停止监听
        server.close();
        udpSocket->close();
        ui->btnStart->setText("启动服务");
        running = false;

        ui->lblServerIP->clear();
        ui->lblServerPort->clear();
    }
}

//读取数据报
void QChatServer::onReadDatagrams(){
    QHostAddress senderIP;
    quint16 senderPort;

    while(udpSocket->hasPendingDatagrams()){
        QByteArray block;
        block.resize(udpSocket->pendingDatagramSize());
        if(-1 == udpSocket->readDatagram(block.data(), block.size(), &senderIP, &senderPort))
        {
            continue;
        }

        processDatagram(block, senderIP.toIPv4Address(), senderPort);
    }
}

//处理数据报
void QChatServer::processDatagram(QByteArray block, quint32 senderIP,
                     quint16 senderPort)
{
    QDataStream in(&block, QIODevice::ReadOnly);
    //quint16 dataGramSize
    quint8 msgType;

    in >> msgType;

    switch(msgType)
    {
    case MSG_CLIENT_CONN: //客户端连接
    {
        quint32 id;
        in >> id;

        ///设置用户状态, IP, Port
        setUserStatus(id, STATUS_ONLINE, senderIP, senderPort);

        //向登陆用户发送用户列表
        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_5_4);

        QList<quint32> listID;
        QStringList listName;
        if(db->getUserList(&listID, &listName) > 0){
            quint8 msgType = MSG_SERVER_USER_ONLINE_LIST;
            out << (quint16) 0 << msgType << listID << listName;
            out.device()->seek(0);
            out<<(quint16)(block.size() - sizeof(quint16));
            udpSocket->writeDatagram(block.data(), block.size(), QHostAddress(senderIP), senderPort);
        }

        ///广播用户状态
        msgType = MSG_SERVER_CHANGE_USER_STATUS;
        block.clear();
        out.device()->seek(0);
        QUser *pUser = onlineUser.value(id); //由在线用户列表获取用户信息
        out << (quint16)0 << msgType << id << pUser->name;
        out.device()->seek(0);
        out << (quint16)(block.size() - sizeof(quint16));
        broadcast(block);

        ///更新显示
        tbviewRefresh();
        ui->txtLog->append(pUser->name + "(" + QString::number(id) + "," +
                           QHostAddress(pUser->ip).toString() + ":" +
                           QString::number(pUser->port) + ") 上线了");
        break;

    }
    case MSG_CLIENT_LOGOUT: //用户退出
    {

        quint32 id;
        in >>id;

        ///广播用户离线
        msgType = MSG_CLIENT_LOGOUT;
        QUser *pUser = onlineUser.value(id);

        block.clear();
        QDataStream out(&block, QIODevice::WriteOnly);
        out << (quint16)0 << msgType << id << pUser->name;
        out.device()->seek(0);
        out << (quint16)(block.size() - sizeof(quint16));
        broadcast(block);

        ///更新显示
        ui->txtLog->append(pUser->name + "(" + QString::number(id) + ")" + "退出了");
        onlineUser.remove(id);
        tbviewRefresh();
        break;
    }
    case MSG_CLIENT_GROUP_CHAT: //聊天消息
    {
        quint32 fromID;
        QString fromName, strInfo;
        in >> fromID >> strInfo;

        QUser *pUser = onlineUser.value(fromID); //由在线用户列表获取用户信息
        fromName = pUser->name;

        QByteArray blockRelay;
        QDataStream out(&blockRelay, QIODevice::WriteOnly);
        msgType = MSG_CLIENT_GROUP_CHAT;
        out << (quint16)0 << msgType << fromID << fromName << strInfo;
        out.device()->seek(0);
        out << (quint16)(blockRelay.size() - sizeof(quint16));
        broadcast(blockRelay);
        break;
    }
    default:
        break;
    }//end of switch

}

/********************************* 在线用户列表 onlineUser操作函数 *********************************/
void setUserStatus(quint32 userid, quint8 status)
{
    QUser *pUser = onlineUser.value(userid);
    if(!pUser){
        pUser = new QUser();
        onlineUser.insert(userid, pUser);
        db->getUserInfo(userid, pUser);
    }
    pUser->status = status;
}

void setUserStatus(quint32 userid, quint8 status, quint32 ip, quint16 port)
{
    QUser *pUser = onlineUser.value(userid);
    if(!pUser){
        pUser = new QUser();
        onlineUser.insert(userid, pUser);
        db->getUserInfo(userid,pUser);
    }
    pUser->status = status;
    pUser->ip = ip;
    pUser->port = port;
}

///向在线用户广播数据
void broadcast(QByteArray block)
{
    foreach(quint32 userid, onlineUser.keys())
    {
        QUser *pUser = onlineUser.value(userid);
        udpSocket->writeDatagram(block.data(), block.size(), QHostAddress(pUser->ip), pUser->port);
    }
}
